

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>2048</title>
    <style>
        *{
            font-family: arial;
            margin: 0;
            padding: 0;
        }
        .header{
            width: 500px;
            height: 50px;
            color: #000;
            margin: 0 auto;
            font-size: 48px;
            font-weight: bold;
            margin-top: 100px;
        }
        .header>span{
            color: #f00;
        }
        .cell_all{
            width: 500px;
            height: 500px;
            margin: 0 auto;
            background-color: #bbada0;
            border-radius: 10px;
        }
        .cell{
            width: 110px;
            height: 110px;
            border: 1px solid #000;
            opacity: 0.95;
            float: left;
            border-radius: 9px;
            margin-left: 10px;
            margin-top: 12px;
            font-size: 35px;
            font-weight: bold;
            line-height: 110px;
            text-align: center;
            color: #fff;
        }
        /*不同数字加减后的数值*/
        .n2{
            background-color:#eee3da;
            color:#776e65;
        }
        .n4{
            background-color:#ede0c8;
            color:#776e65;
        }
        .n8{
            background-color:#f2b179;
        }
        .n16{
            background-color:#f59563;
        }
        .n32{
            background-color:#f67c5f;
        }
        .n64{
            background-color:#f65e3b;
        }
        .n128{
            background-color:#edcf72;
        }
        .n256{
            background-color:#edcc61;
        }
        .n512{
            background-color:#9c0;
        }
        .n1024{
            font-size:40px;
            background-color:#33b5e5;
        }
        .n2048{
            font-size:40px;
            background-color: #09c;
        }
        .n4096{
            background-color:#a6c;
            font-size:40px;
        }
        .n8192{
            background-color:#93c;
            font-size:40px;
        }

        /*gameover*/
        .gameover{
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: rgba(0,0,0,0.3);
            display: none;
        }
        .gameover>p{
            width: 300px;
            height: 200px;
            background-color: #fff;
            border: 1px solid #000;
            border-radius: 15px;
            position: absolute;
            left: 50%;
            top: 50%;
            margin-top: -100px;
            margin-left: -150px;

            font-size: 30px;
            font-weight: bold;
            text-align: center;
            line-height: 66px;
        }

        .gameover_p>span{
            color: #f00;
            font-size: 40px;
        }

        .gameover_p>a{
            text-decoration: none;
            background-color: #9f8d77;
            border-radius: 5px;
            color: #fff;
            padding: 8px 8px 4px;
        }
    </style>
</head>
<body>
<!-- header -->
<div class="header">
    SCORE:<span id="score_01">0</span>
</div>

<!-- 4*4 -->
<div class="cell_all">
    <!-- 第一行 -->
    <div class="cell" id="c00"></div>
    <div class="cell" id="c01"></div>
    <div class="cell" id="c02"></div>
    <div class="cell" id="c03"></div>
    <!-- 第二行 -->
    <div class="cell" id="c10"></div>
    <div class="cell" id="c11"></div>
    <div class="cell" id="c12"></div>
    <div class="cell" id="c13"></div>
    <!-- 第三行 -->
    <div class="cell" id="c20"></div>
    <div class="cell" id="c21"></div>
    <div class="cell" id="c22"></div>
    <div class="cell" id="c23"></div>
    <!-- 第四行 -->
    <div class="cell" id="c30"></div>
    <div class="cell" id="c31"></div>
    <div class="cell" id="c32"></div>
    <div class="cell" id="c33"></div>
    <!--[
            [00,01,02,03],
            [10,11,12,13],
            [20,21,22,23],
            [30,31,32,33]
    ]-->
</div>

<!-- gameover -->
<div class="gameover" id="gameover">
    <p class="gameover_p">
        GAMEOVER！<br>
        SCORE:<span id="score_02">0</span><br>
        <a href="javascript:game.start()">TRY AGAIN!</a>
    </p>
</div>
</body>
<script>
    //创建一个对象,所有的操作全部都在对象里面完成
    var game = {
        data:[],//存放数据
        score:0,//分数
        status:1,// 定义游戏状态：可以玩的时候为状态为：1
        gameover:0,//游戏结束的状态定义为0
        gamerunning:1,//表示的是当前游戏的状态，时时刻刻检测该状态，如果这个状态等于1，表示可以继续，如果等于0，结束

        // 游戏开始
        start: function(){
            game.gamerunning = game.status;
            game.score = 0; //游戏开始时分数清零
            game.data = [ //重新给数组进行赋值
                [0,0,0,0],
                [0,0,0,0],
                [0,0,0,0],
                [0,0,0,0]
            ]
            // 生成两个随机数
            game.randomNum();
            game.randomNum();
            // 生成游戏开始时的视图
            game.dataView();
        },
        randomNum:function(){//生成随机数的方法
            //这个方法,需要用到的时候调用,不需要用到的时候不用
            //数字要随机生成2或者4;位置也要随机生成
            for(;;){//死循环，直到生成完成再停止，否则一直生成
                var r = Math.floor(Math.random()*4);//生成一个行
                var c = Math.floor(Math.random()*4);//生成一个列
                if(game.data[r][c] == 0){//只有数组这一个位置为 0 的时候才可以赋值
                    var num = Math.random()>0.5?2:4; //随机生成2或者4
                    game.data[r][c] = num;
                    break;//退出死循环
                }
            }
        },
        dataView:function(){    //更新视图的方法
            for (var r = 0; r < 4; r++) {
                for(var c = 0; c<4;c++){
                    // console.log(game.data[r][DragonRun]);
                    var div = document.getElementById("c" + r + c); //获取到每一个对应的div
                    if(game.data[r][c] != 0){   //如果数组里有数值
                        div.innerHTML = game.data[r][c];//给单元格进行赋值
                        div.className = "cell n" + game.data[r][c]; //改变样式
                    }else{  //如果数组里不含有元素
                        div.innerHTML = "";//数组中的元素如果为0的时候，界面中是没有值的，把内容清空
                        div.className = "cell";//样式恢复
                    }
                }
            }

            document.getElementById("score_01").innerText = game.score; //找到分数的ID，更新分数

            if(game.gamerunning == game.gameover){  //如果游戏处于结束的阶段了，更新视图
                document.getElementById("gameover").style.display = "block";//在进行中 对某一状态进行直接的改变 "游戏结束时，将结束时对应写好的'框'display:显示出来'block'"
                document.getElementById("score_02").innerText = game.score;//找到结束时的分数ID，更新分数
            }else{//如果游戏处于游戏状态中
                document.getElementById("gameover").style.display = "none";
            }// 定义游戏的结束状态和游戏时的状态，根据状态来确定何时弹出对应状态的'display:block || none'
        },
        isgameover:function(){  //  判断游戏是否结束的方法
            // 那，游戏结束需要满足哪些条件呢？
            // 1.格子全部都不为0的时候 && 相邻的位置不能有相同的
            // 给函数增加一个返回值,如果false表示还可以继续,ture表示游戏结束
            for(var r = 0;r<4;r++){
                for(var c = 0;c<4;c++){
                    if(game.data[r][c] == 0){   //如果有等于零的情况，则游戏继续
                        return false;
                    }
                    if(c<3){//检测同一行之间是否有相同
                        if(game.data[r][c] == game.data[r][c+1]){
                            return false;
                        }
                    }
                    if(r<3){//检测同一列之间是否有相同
                        if(game.data[r][c] == game.data[r+1][c]){
                            return false;
                        }
                    }

                }
            }
            return true;//如果条件全都不满足，表示游戏结束
        },

        //移动的方法：
        //左移
        moveLeft:function(){
            var before = String(game.data);//给移动之前的数组“拍个照”      因为数组之间不能直接比较(地址问题),所以这里需要先将其转化为字符串,然后才能用于比较
            for(var r = 0;r<4;r++){
                game.moveLeftinRow(r);
            }
            var after = String(game.data);//给移动之后的数组“拍个照”
            // 比较“拍照”前的前后
            if(before != after){    //  若是前后不相等
                game.randomNum();   //生成一个随机数
                if(game.isgameover()){
                    game.gamerunning == game.gameover;      //游戏状态改变为gameover
                }
                game.dataView();//更新视图
            }
        },
        moveLeftinRow:function(r){
            for(var c = 0;c<3;c++){
                var nextc = game.moveLeftNextRows(r,c);//找数字,并依据不同的情况返回值
                if(nextc != -1){//在位置game.data[r][i]上找到了元素
                    if(game.data[r][c] == 0){//如果为0,则替代
                        game.data[r][c] = game.data[r][nextc];
                        game.data[r][nextc] = 0;
                        c--;
                    }else if(game.data[r][c] == game.data[r][nextc]){
                        game.data[r][c] *= 2;
                        game.score += game.data[r][c];
                        game.data[r][nextc] = 0;
                    }
                }else{//如果没找到
                    break;
                }
            }
        },
        moveLeftNextRows:function(r,c){//找位置的函数
            for (var i = c+1;i<4;i++) {//从当前位置的下一个元素开始去找,所以是 DragonRun+1(当前位置为c，下一个为c+1)
                if (game.data[r][i] != 0) {//如果不等于0，则表明位置上存在一个元素
                    return i;//找到了就把这个位置直接返回出来
                }
            }
            return -1;//如果没有找到，随意取一个标识符返回回来:表示没有找到
        },
        //  左移结束
        ///////////////////////////////////////////////////////////////////////////////////
        //  右移开始
        moveRight:function(){
            var before = String(game.data);
            for (var r = 0;r<4;r++) {//逐行比较
                game.moveRightinRow(r);
            }
            var after = String(game.data);
            if (before != after) {
                game.randomNum();//生成一个随机数
                if(game.isgameover()){//判断游戏是否结束
                    game.gamerunning = game.gameover;
                }
                game.dataView();//更新视图
            }
        },
        moveRightinRow:function(r){
            for(var c = 3;c>0;c--){
                var nextc = game.moveRightNextRows(r,c);//找数字,并根据找数字的情况接收返回值
                if (nextc != -1) {//如果返回值不为 -1,则表示位置上找到了元素
                    if(game.data[r][c] == 0){
                        game.data[r][c] = game.data[r][nextc];
                        game.data[r][nextc] = 0;
                        c++;// 这一步,重点理解: 当位置替代发生了改变之后,下一次的比较则从下一位开始比较,需要让它回到原点的位置重新从头开始继续比较
                    }else if (game.data[r][c] == game.data[r][nextc]) {
                        game.data[r][c] *= 2;
                        game.score += game.data[r][c];
                        game.data[r][nextc] = 0;
                    }
                }else{
                    break;
                }
            }
        },
        moveRightNextRows:function(r,c){//找位置的函数
            for(var i = c-1;i>=0;i--){//因为从c开始,所以下一位是从 DragonRun-1 开始找
                if (game.data[r][i] != 0) {
                    return i;
                }
            }
            return -1;
        },
//  右移结束

        moveTop:function(){//上移的方法
            var before = String(game.data);//强制转换成字符串  移动前给数组拍一个照  //join toString()
//      移动的方法
            for(var c = 0;c<4;c++){//移动每一列
                game.moveTopinRow(c);
            }
            var after = String(game.data);//强制转换成字符串  移动以后给数组拍一个照   ?

            if(before != after){//如果两次拍照不相等，就发生了移动
                game.randomNum();//生成一个随机数
                if(game.isgameover()){//如果游戏结束
                    game.gamerunning = game.gameover;//改变游戏的状态
                }
                game.dataView();//移动完成数据更新
            }

        },
        moveTopinRow:function(c){//仅仅移动一行
            for(var r = 0;r<4;r++){
                var nextr = game.moveTopNextRows(r,c);//获取到后面数字的位置   1 2 3  -1
                if(nextr != -1){//如果后面找到元素
                    if(game.data[r][c] == 0){//如果为0，就替代
                        game.data[r][c] = game.data[nextr][c];//
                        game.data[nextr][c] = 0;
                        r--;//从原地开始，继续往后
                    }else if(game.data[r][c] == game.data[nextr][c]){//如果相等
                        game.data[r][c] *= 2;//当前位置的值*2
                        game.score += game.data[r][c];//加上分数
                        game.data[nextr][c] = 0;//位置变为0
                    }
                }else{//如果没有找到元素
                    break;//直接退出循环
                }
            }
        },
        moveTopNextRows:function(r,c){
            for(var i = r + 1;i < 4;i++){
                //如果不为0，表示找到，就返回i，i对应的格子的几个位置
                if (game.data[i][c] != 0) {
                    return i;
                }
            }
            return -1//表示的是后面都找不到元素。表示返回一个状态，这个状态自己定义，只要后面对应就OK
        },
        //////////////////////////////////////////////////////////////////////////////////////////

        moveDown:function(){//下移的方法
            var before = String(game.data);//强制转换成字符串  移动前给数组拍一个照  //join toString()
//      移动的方法
            for(var c = 0;c<4;c++){//移动每一列
                game.moveDowninRow(c);
            }
            var after = String(game.data);//强制转换成字符串  移动以后给数组拍一个照   ?

            if(before != after){//如果两次拍照不相等，就发生了移动
                game.randomNum();//生成一个随机数
                if(game.isgameover()){//如果游戏结束
                    game.gamerunning = game.gameover;//改变游戏的状态
                }
                game.dataView();//移动完成数据更新
            }

        },
        moveDowninRow:function(c){//仅仅移动一行
            for(var r = 3;r>=0;r--){
                var nextr = game.moveDownNextRows(r,c);//获取到后面数字的位置   1 2 3  -1
                if(nextr != -1){//如果后面找到元素
                    if(game.data[r][c] == 0){//如果为0，就替代
                        game.data[r][c] = game.data[nextr][c];//
                        game.data[nextr][c] = 0;
                        r++;//从原地开始，继续往后
                    }else if(game.data[r][c] == game.data[nextr][c]){//如果相等
                        game.data[r][c] *= 2;//当前位置的值*2
                        game.score += game.data[r][c];//加上分数
                        game.data[nextr][c] = 0;//位置变为0
                    }
                }else{//如果没有找到元素
                    break;//直接退出循环
                }
            }
        },
        moveDownNextRows:function(r,c){
            for(var i = r - 1;i >= 0;i--){
                //如果不为0，表示找到，就返回i，i对应的格子的几个位置
                if (game.data[i][c] != 0) {
                    return i;
                }
            }
            return -1//表示的是后面都找不到元素。表示返回一个状态，这个状态自己定义，只要后面对应就OK
        }
    }

    game.start();
    document.onkeydown = function(e){
        var e = e || event || arguments[0];
        if(e.keyCode == 37){
            game.moveLeft();
        }else if (e.keyCode == 39) {
            game.moveRight();
        }else if (e.keyCode == 38) {
            game.moveTop();
        }else if (e.keyCode == 40) {
            game.moveDown();
        }
    }
</script>
<script>
    !function(){
        function n(n,e,t){
            return n.getAttribute(e)||t
        }
        function e(n){
            return document.getElementsByTagName(n)
        }
        function t(){
            var t=e("script"),o=t.length,i=t[o-1];
            return{l:o,z:n(i,"zIndex",-1),o:n(i,"opacity",.5),c:n(i,"color","0,0,0"),n:n(i,"count",99)}
        }
        function o(){
            a=m.width=window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth,
                c=m.height=window.innerHeight||document.documentElement.clientHeight||document.body.clientHeight
        }
        function i(){
            r.clearRect(0,0,a,c);
            var n,e,t,o,m,l;
            s.forEach(function(i,x){
                for(i.x+=i.xa,i.y+=i.ya,i.xa*=i.x>a||i.x<0?-1:1,i.ya*=i.y>c||i.y<0?-1:1,r.fillRect(i.x-.5,i.y-.5,1,1),e=x+1;e<u.length;e++)n=u[e],
                null!==n.x&&null!==n.y&&(o=i.x-n.x,m=i.y-n.y,
                    l=o*o+m*m,l<n.max&&(n===y&&l>=n.max/2&&(i.x-=.03*o,i.y-=.03*m),
                    t=(n.max-l)/n.max,r.beginPath(),r.lineWidth=t/2,r.strokeStyle="rgba("+d.c+","+(t+.2)+")",r.moveTo(i.x,i.y),r.lineTo(n.x,n.y),r.stroke()))
            }),
                x(i)
        }
        var a,c,u,m=document.createElement("canvas"),
            d=t(),l="c_n"+d.l,r=m.getContext("2d"),
            x=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||
                function(n){
                    window.setTimeout(n,1e3/45)
                },
            w=Math.random,y={x:null,y:null,max:2e4};m.id=l,m.style.cssText="position:fixed;top:0;left:0;z-index:"+d.z+";opacity:"+d.o,e("body")[0].appendChild(m),o(),window.onresize=o,
            window.onmousemove=function(n){
                n=n||window.event,y.x=n.clientX,y.y=n.clientY
            },
            window.onmouseout=function(){
                y.x=null,y.y=null
            };
        for(var s=[],f=0;d.n>f;f++){
            var h=w()*a,g=w()*c,v=2*w()-1,p=2*w()-1;s.push({x:h,y:g,xa:v,ya:p,max:6e3})
        }
        u=s.concat([y]),
            setTimeout(function(){i()},100)
    }();
</script>
</html>
